﻿using RabbitMQ.Client;

namespace Tszy.Unlimited.Base.Infrastructure.Queues.RabbitMQ;

/// <summary>
/// RabbitMQ 服务。
/// </summary>
public interface IRabbitMQService : IRabbitMQPublisher, IRabbitMQConsumer, IDisposable
{
    /// <summary>
    /// 注册路由 KEY。
    /// </summary>
    /// <param name="routeKey">路由 KEY。</param>
    void Register(string routeKey);

    /// <summary>
    /// 监听。
    /// </summary>
    void Listen();
}

/// <summary>
/// RabbitMQ 服务。
/// </summary>
public class RabbitMQService : IRabbitMQService
{
    private readonly RabbitMQConfig _config;

    private IModel _channel;

    private readonly IRabbitMQConnectionBuilder _builder;
    private IRabbitMQConsumer? _consumer;
    private IRabbitMQPublisher _publisher;

    /// <summary>
    /// 初始化。
    /// </summary>
    /// <param name="exchangeName">交换机名称。</param>
    /// <param name="queueName">队列名称。</param>
    public RabbitMQService(string exchangeName, string queueName)
        : this(new ConnectionFactory { HostName = "localhost" }, new RabbitMQConfig(exchangeName, queueName))
    { }

    /// <summary>
    /// 初始化。
    /// </summary>
    /// <param name="config">RabbitMQ 配置。</param>
    public RabbitMQService(RabbitMQConfig config)
        : this(new ConnectionFactory { HostName = "localhost" }, config)
    { }

    /// <summary>
    /// 初始化。
    /// </summary>
    /// <param name="hostName">主机名称。</param>
    /// <param name="port">端口号。</param>
    /// <param name="userName">用户名。</param>
    /// <param name="password">密码。</param>
    /// <param name="exchangeName">交换机名称。</param>
    /// <param name="queueName">队列名称。</param>
    public RabbitMQService(string hostName, int port, string userName, string password, string exchangeName, string queueName) : this(
        hostName, port, userName, password, new RabbitMQConfig(exchangeName, queueName))
    {
    }

    /// <summary>
    /// 初始化。
    /// </summary>
    /// <param name="hostName">主机名称。</param>
    /// <param name="port">端口号。</param>
    /// <param name="userName">用户名。</param>
    /// <param name="password">密码。</param>
    /// <param name="config">RabbitMQ 配置。</param>
    public RabbitMQService(string hostName, int port, string userName, string password, RabbitMQConfig config)
    {
        _config = config;
        _builder = new RabbitMQConnectionBuilder(hostName, port, userName, password);

        _channel = _builder.Build();
        _channel.QueueDeclare(_config.QueueName, _config.Durable, false, false, null);
        _channel.ExchangeDeclare(_config.ExchangeName, _config.ExchangeType, _config.Durable);

        _publisher = new RabbitMQPublisher(_config, _channel);
    }

    /// <summary>
    /// 初始化。
    /// </summary>
    /// <param name="connectionFactory">RabbitMQ 连接工厂。</param>
    /// <param name="config">RabbitMQ 配置。</param>
    public RabbitMQService(IConnectionFactory connectionFactory, RabbitMQConfig config)
    {
        _config = config;
        _builder = new RabbitMQConnectionBuilder(connectionFactory);

        _channel = _builder.Build();
        _channel.QueueDeclare(_config.QueueName, _config.Durable, false, false, null);
        _channel.ExchangeDeclare(_config.ExchangeName, _config.ExchangeType, _config.Durable);

        _publisher = new RabbitMQPublisher(_config, _channel);
    }

    /// <summary>
    /// 接收到消息时触发的事件。
    /// </summary>
    public event EventHandler<RabbitMQRecivedEventArgs>? Received;

    /// <summary>
    /// 如果是手动应答，则在应答执行前触发此事件。
    /// </summary>
    public event EventHandler<RabbitMQRecivedEventArgs>? AutoAckExecuting;

    /// <summary>
    /// 如果是手动应答，则在应答执行后触发此事件。
    /// </summary>
    public event EventHandler<RabbitMQRecivedEventArgs>? AutoAckExecuted;

    /// <summary>
    /// 监听。
    /// </summary>
    public virtual void Listen()
    {
        _consumer = new RabbitMQConsumer(_config, _channel);
        _consumer.Received += (s, e) => Received?.Invoke(s, e);
        _consumer.AutoAckExecuting += (s, e) => AutoAckExecuting?.Invoke(s, e);
        _consumer.AutoAckExecuted += (s, e) => AutoAckExecuted?.Invoke(s, e);
    }

    /// <summary>
    /// 注册路由 KEY。
    /// </summary>
    /// <param name="routeKey">路由 KEY。</param>
    public void Register(string routeKey)
    {
        _channel.QueueBind(_config.QueueName, _config.ExchangeName, routeKey);
    }

    /// <summary>
    /// 向指定的路由 KEY 发送消息。
    /// </summary>
    /// <param name="routeKey">路由 KEY。</param>
    /// <param name="data">发送内容。</param>
    public void Send(string routeKey, string data)
    {
        Register(routeKey);
        _publisher.Send(routeKey, data);
    }

    #region 释放资源

    /// <summary>
    /// 释放资源
    /// </summary>
    public void Dispose()
    {
        _builder.Dispose();
        GC.SuppressFinalize(this);
    }

    #endregion 释放资源
}